﻿namespace Ngs.Data
{
    #region Using Directives
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;
    using Ngs.Reflection;
    #endregion

    public class ReferenceMapper<TSource, TDestination, TSubSource, TSubDestination> : AssociationMapper<TSource, TDestination, TSubSource, TSubDestination>, IReferenceMapper<TSource, TDestination, TSubSource, TSubDestination>
    {

        public ReferenceMapper()
        {
            this.ReferenceSetter = new Action<TDestination, TSubDestination>(this.SetValue);
        }

        #region Properties

        public Func<TSource, TSubSource> ReferenceGetter { get; set; }

        public Action<TDestination, TSubDestination> ReferenceSetter { get; set; }

        #endregion

        /// <summary>
        /// Used if an explicit Setter is not provided (ReferenceSetter property).
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="value"></param>
        protected virtual void SetValue(TDestination instance, TSubDestination value)
        {
            ExceptionFactory.ThrowInvalidOperation(this.Member == null, "Member is null.");

            PropertyInfo property = this.Member as PropertyInfo;

            ExceptionFactory.ThrowInvalidOperation(property == null, "Member is not a property.");
            ExceptionFactory.ThrowInvalidOperation(!property.CanWrite, string.Format("{0} is readonly.", property.Name));

            // Replaced with lambda expression implementation
            var setter = property.SetValue<TDestination>();
            setter(instance, value);
            //property.SetValue(instance, value, null);
        }

        protected override void ExecuteCore(TSource entity, TDestination obj)
        {
            TSubSource subSource = this.ReferenceGetter(entity);
            if (subSource != null)
            {
                TSubDestination domainObj = FactoryMethod();
                ReferenceMap(subSource, domainObj);
                this.ReferenceSetter(obj, domainObj);
            }
        }

    }
}
